require "lib/formats"

module MediaTools
  
  class ConverterFactory
    def self.register_converter(converter_class)
      (@converters ||= []) << converter_class.new
    end

    def self.create_converter(source_container, dest_container)
      converter = @converters.find do |obj|
        obj.can_convert? source_container, dest_container
      end
      raise RuntimeError.new("converter not found for: #{source_container} to #{dest_container}") unless converter
      converter
    end
  end
    
  module Converters
    
    module Converter
      def can_convert?(src_container, dest_container)
        raise NotImplementedError.new
      end
      
      def convert
        raise NotImplementedErrot.new
      end
      
      def self.included(othermod)
        ConverterFactory.register_converter othermod if othermod.instance_of? Class
      end
    end
    
    module CommandLineConverter
      include Containers
      include Formats
      
      include Container
      
      module ClassMethods
        def command(cmd_args)
          @cmd_args = cmd_args
        end

        def converts_from(src_container_class, src_format_class = nil)
          @src_container = src_container_class.new
          @src_container.format = src_format_class.matcher if src_format_class
        end

        def converts_to(dest_container_class, dest_format_class = nil)
          raise RuntimeError.new("converts_to must be preceeded by converts_from") unless @src_container
          _src_container = @src_container
          _dest_container = dest_container_class.new
          _dest_container.format = dest_format_class.matcher if dest_format_class

          define_method :can_convert? do |src_container, dest_container|
            _src_container == src_container && (_src_container.format.nil? || _src_container.format == src_container.format) &&
              _dest_container == dest_container && (_dest_container.format.nil? || _dest_container.format == dest_container.format)
          end
        end  

      end
      
      alias_method :initialize_without_command_line_converter, :initialize
      def initialize(*args)
        super(*args)
        @executor = Kernel.method(:system)
      end
      attr :executor, true

      def self.included(othermod)
        othermod.extend(ClassMethods)
        Converter.included(othermod)
      end
      
      def convert(src_path, dest_path)
        cmd_args = self.class.instance_variable_get(:@cmd_args)
        cmd_args.map! { |arg| (arg == "$src" && src_path) || (arg == "$dest" && dest_path) || arg}
        @executor.call(*cmd_args)
      end
    end
    
    class FlacToPcmWavConverter
      include CommandLineConverter
      
      converts_from FlacContainer
      converts_to WavContainer, PcmWavFormat
      
      command %w(flac -d -o $dest -w -- $src)
    end
    
    class PcmWavToFlacConverter
      include CommandLineConverter
      
      converts_from WavContainer, PcmWavFormat
      converts_to FlacContainer
      
      command %w(flac -o $dest --best -w -- $src)
    end

    class ApeToPcmWavConverter
      include CommandLineConverter
      
      converts_from ApeContainer
      converts_to WavContainer, PcmWavFormat
      
      command %w(ffmpeg -i $src $dest)
    end
    
    class WavpackToPcmWavConverter
      include CommandLineConverter
      
      converts_from WavpackContainer
      converts_to WavContainer, PcmWavFormat
      
      command "wvunpack $src -o $dest"
    end
    
    class PcmWavToMp3Converter
      include CommandLineConverter
      
      converts_from WavContainer, PcmWavFormat
      converts_to FlacContainer
    end
        
  end
  
end